//
//  QCMidiPluginPlugIn.m
//  QCMidiPlugin
//
//  Created by Math Wrath on 7/15/10.
//  Copyright (c) 2010 __MyCompanyName__. All rights reserved.
//
#import <OpenGL/CGLMacro.h>

#include <CoreServices/CoreServices.h> //for file stuff
#include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/AudioToolbox.h> //for AUGraph
#include <unistd.h> // used for usleep...

#import "QCMidiPluginPlugIn.h"


OSStatus	CreateAUGraph (AUGraph &outGraph, AudioUnit &outSynth)
{
	OSStatus result;

	AUNode synthNode, limiterNode, outNode;
	
	AudioComponentDescription cd;
	cd.componentManufacturer = kAudioUnitManufacturer_Apple;
	cd.componentFlags = 0;
	cd.componentFlagsMask = 0;
	
	require_noerr (result = NewAUGraph (&outGraph), home);
	
	cd.componentType = kAudioUnitType_MusicDevice;
	cd.componentSubType = kAudioUnitSubType_DLSSynth;
	
	require_noerr (result = AUGraphAddNode (outGraph, &cd, &synthNode), home);
	
	cd.componentType = kAudioUnitType_Effect;
	cd.componentSubType = kAudioUnitSubType_PeakLimiter;  
	
	require_noerr (result = AUGraphAddNode (outGraph, &cd, &limiterNode), home);
	
	cd.componentType = kAudioUnitType_Output;
	cd.componentSubType = kAudioUnitSubType_DefaultOutput;  
	require_noerr (result = AUGraphAddNode (outGraph, &cd, &outNode), home);
	
	require_noerr (result = AUGraphOpen (outGraph), home);
	
	require_noerr (result = AUGraphConnectNodeInput (outGraph, synthNode, 0, limiterNode, 0), home);
	require_noerr (result = AUGraphConnectNodeInput (outGraph, limiterNode, 0, outNode, 0), home);
	
	// ok we're good to go - get the Synth Unit...
	require_noerr (result = AUGraphNodeInfo(outGraph, synthNode, 0, &outSynth), home);
	
home:
	return result;
}


// some MIDI constants:
enum {
	kMidiMessage_ControlChange 		= 0xB,
	kMidiMessage_ProgramChange 		= 0xC,
	kMidiMessage_BankMSBControl 	= 0,
	kMidiMessage_BankLSBControl		= 32,
	kMidiMessage_NoteOn 			= 0x9
};



#define	kQCPlugIn_Name				@"QCMidiPlugin"
#define	kQCPlugIn_Description		@"QCMidiPlugin MIDI Patch by MATHWRATH"

@implementation QCMidiPluginPlugIn


@dynamic inputTrigger;
@dynamic pitchAmount;
@dynamic velocityAmount;
@dynamic instrumentChg;

+ (NSDictionary*) attributes
{
	
	
	return [NSDictionary dictionaryWithObjectsAndKeys:kQCPlugIn_Name, QCPlugInAttributeNameKey, kQCPlugIn_Description, QCPlugInAttributeDescriptionKey, nil];
}

+ (NSDictionary*) attributesForPropertyPortWithKey:(NSString*)key
{
	
	if([key isEqualToString:@"inputTrigger"])
        return [NSDictionary dictionaryWithObjectsAndKeys:
                @"Note_On", QCPortAttributeNameKey,
                @"NO", QCPortAttributeDefaultValueKey,
                nil];
	
	if([key isEqualToString:@"pitchAmount"])
        return [NSDictionary dictionaryWithObjectsAndKeys:
                @"Note_Pitch", QCPortAttributeNameKey,
                @"60", QCPortAttributeDefaultValueKey,
                nil];
	
	if([key isEqualToString:@"velocityAmount"])
        return [NSDictionary dictionaryWithObjectsAndKeys:
                @"Velocity_Amount", QCPortAttributeNameKey,
                @"127", QCPortAttributeDefaultValueKey,
                nil];
	
	if([key isEqualToString:@"instrumentChg"])
        return [NSDictionary dictionaryWithObjectsAndKeys:
                @"Instrument_Choice", QCPortAttributeNameKey,
                @"3", QCPortAttributeDefaultValueKey,
                nil];
	
	return nil;
}

+ (QCPlugInExecutionMode) executionMode
{
	
	return kQCPlugInExecutionModeConsumer;
}

+ (QCPlugInTimeMode) timeMode
{
	
	return kQCPlugInTimeModeTimeBase;
}

- (id) init
{
	if(self = [super init]) {
		
		graph = 0;
		synthUnit;
		bankPath = 0;
		result=noErr;

		midiChannelInUse = 0; 
		
		
		verify_noerr (result = CreateAUGraph (graph, synthUnit));
		
		
		
		
		verify_noerr (result = FSPathMakeRef ((const UInt8*)bankPath, &fsRef, 0));
			
		verify_noerr (result = AudioUnitSetProperty (synthUnit,
														  kMusicDeviceProperty_SoundBankFSRef,
														  kAudioUnitScope_Global, 0,
														  &fsRef, sizeof(fsRef)));
			
		
		
		verify_noerr (result = AUGraphInitialize (graph));
		
		
		verify_noerr (result = MusicDeviceMIDIEvent(synthUnit, 
													 kMidiMessage_ControlChange << 4 | midiChannelInUse, 
													 kMidiMessage_BankMSBControl, 0,
													 0));
		
		verify_noerr (result = MusicDeviceMIDIEvent(synthUnit, 
													 kMidiMessage_ProgramChange << 4 | midiChannelInUse, 
													 4, 0,
													 0));
		
		CAShow (graph);
		verify_noerr (result = AUGraphStart (graph));
	}
	
	return self;
}

- (void) finalize
{
	
	[super finalize];
}

- (void) dealloc
{
	DisposeAUGraph (graph);
	[super dealloc];
}

@end

@implementation QCMidiPluginPlugIn (Execution)

- (BOOL) startExecution:(id<QCPlugInContext>)context
{
	
	return YES;
}

- (void) enableExecution:(id<QCPlugInContext>)context
{
	graph = 0;
	synthUnit;
	
	bankPath = 0;
	
	midiChannelInUse = 0; 
	result=noErr;

	
	
	
	verify_noerr (result = CreateAUGraph (graph, synthUnit));
	
	
	
	
	verify_noerr (result = FSPathMakeRef ((const UInt8*)bankPath, &fsRef, 0));
	
	verify_noerr (result = AudioUnitSetProperty (synthUnit,
												 kMusicDeviceProperty_SoundBankFSRef,
												 kAudioUnitScope_Global, 0,
												 &fsRef, sizeof(fsRef)));
	


	verify_noerr (result = AUGraphInitialize (graph));


	verify_noerr (result = MusicDeviceMIDIEvent(synthUnit, 
											kMidiMessage_ControlChange << 4 | midiChannelInUse, 
											kMidiMessage_BankMSBControl, 0,
											0));

	verify_noerr (result = MusicDeviceMIDIEvent(synthUnit, 
											kMidiMessage_ProgramChange << 4 | midiChannelInUse, 
											4, 0,
											0));

	CAShow (graph);
	verify_noerr (result = AUGraphStart (graph));

}

- (BOOL) execute:(id<QCPlugInContext>)context atTime:(NSTimeInterval)time withArguments:(NSDictionary*)arguments
{
	int i = 0;
	

	UInt32 programChg = self.instrumentChg;
	UInt32 noteNum = self.pitchAmount;
	UInt32 onVelocity = self.velocityAmount;
	UInt32 noteOnCommand = 	kMidiMessage_NoteOn << 4 | midiChannelInUse;
	
	verify_noerr (result = MusicDeviceMIDIEvent(synthUnit, 
												 kMidiMessage_ProgramChange << 4 | midiChannelInUse, 
												 programChg, 0,
												 0));
	
	if(self.inputTrigger==true&&i<1){
	verify_noerr (result = MusicDeviceMIDIEvent(synthUnit, noteOnCommand, noteNum, onVelocity, 0));
	
	usleep (1 * 1000 * 1000);
	
	verify_noerr (result = MusicDeviceMIDIEvent(synthUnit, noteOnCommand, noteNum, 0, 0));
		i++;
	}
	else {
		i=0;
	}
	return YES;
}

- (void) disableExecution:(id<QCPlugInContext>)context
{
	
}

- (void) stopExecution:(id<QCPlugInContext>)context
{
	AUGraphStop (graph);
}

@end
